/*
 * Copyright (c) 2010 Philip Foulkes <philip.foulkes@gmail.com>
 *
 * This file is free software and is made available under the
 * terms of the GNU General Public License, version 2, or at your
 * option, any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/slab.h>
#include <linux/hardirq.h>

#include "mrp_private.h"
#include "mrp_private.h"
#include "mrp_state_machine.h"

struct mrp_state_machine * mrp_state_machine_create_and_init (int current_state, struct mrp_state_machine_ops * mrp_state_machine_ops, char * name)
{
	struct mrp_state_machine * mrp_state_machine;

	if ( ! (mrp_state_machine = kzalloc (sizeof(struct mrp_state_machine), MRP_GET_CONTEXT_ALLOC_FLAG))
		|| mrp_state_machine_init (mrp_state_machine, current_state, mrp_state_machine_ops, name) )
	{
		mrp_state_machine_destroy (&mrp_state_machine);
		return NULL;
	}

	return mrp_state_machine;
}

int mrp_state_machine_init (struct mrp_state_machine * mrp_state_machine, int current_state, struct mrp_state_machine_ops * mrp_state_machine_ops, char * name)
{
	if ( 	! mrp_state_machine_ops
		||	! mrp_state_machine_ops->mrp_state_machine_perform_protocol_action
		||  ! mrp_state_machine_ops->mrp_state_machine_get_new_state
		||  ! mrp_state_machine_ops->mrp_state_machine_get_new_protocol_action1
		||  ! mrp_state_machine_ops->mrp_state_machine_get_new_protocol_action2	)
	{
		return -EINVAL;
	}

	INIT_LIST_HEAD (&mrp_state_machine->request_opportunity_list);
	mrp_state_machine->current_state = current_state;
	mrp_state_machine->mrp_state_machine_ops = mrp_state_machine_ops;
	mrp_state_machine->name = name;

	return 0;
}

void mrp_state_machine_destroy (struct mrp_state_machine ** mrp_state_machine)
{
	if (*mrp_state_machine)
	{
		mrp_state_machine_clean_up (*mrp_state_machine);
		kfree (*mrp_state_machine);
		*mrp_state_machine = NULL;
	}
}

void mrp_state_machine_clean_up (struct mrp_state_machine * mrp_state_machine)
{ // nothing to do now - here for completness
}

int mrp_state_machine_get_current_state (struct mrp_state_machine * mrp_state_machine)
{
	return mrp_state_machine->current_state;
}

int mrp_state_machine_receive_protocol_event (struct mrp_state_machine * mrp_state_machine, int mrp_protocol_event)
{
	int res;
	int old_state;
	int new_state;
	int protocol_action1 = MRP_PROTO_ACTION_NONE;
	int protocol_action2 = MRP_PROTO_ACTION_NONE;

	if ( 	(res = mrp_state_machine->mrp_state_machine_ops->mrp_state_machine_get_new_state (mrp_protocol_event, mrp_state_machine_get_current_state (mrp_state_machine), &new_state))
		 || (res = mrp_state_machine->mrp_state_machine_ops->mrp_state_machine_get_new_protocol_action1 (mrp_protocol_event, mrp_state_machine_get_current_state (mrp_state_machine), &protocol_action1))
		 || (res = mrp_state_machine->mrp_state_machine_ops->mrp_state_machine_get_new_protocol_action2 (mrp_protocol_event, mrp_state_machine_get_current_state (mrp_state_machine), &protocol_action2))  )
	{
		return res;
	}

	old_state = mrp_state_machine->current_state;
	mrp_state_machine->current_state = new_state;

	if (old_state != new_state && mrp_state_machine->mrp_state_machine_ops->mrp_state_machine_state_changed)
		mrp_state_machine->mrp_state_machine_ops->mrp_state_machine_state_changed (mrp_state_machine, old_state, new_state);

	/*MRP_PRINT_INFO ("name: %s\n", mrp_state_machine->name);
	MRP_PRINT_INFO ("proto event: %i\n", mrp_protocol_event);
	MRP_PRINT_INFO ("old state: %i\n", old_state);
	MRP_PRINT_INFO ("new state: %i\n", new_state);
	MRP_PRINT_INFO ("proto action 1: %i\n", protocol_action1);
	MRP_PRINT_INFO ("proto action 2: %i\n", protocol_action2);*/

	if (protocol_action1 != MRP_PROTO_ACTION_NONE)
		mrp_state_machine->mrp_state_machine_ops->mrp_state_machine_perform_protocol_action (mrp_state_machine, protocol_action1);

	if (unlikely (protocol_action2 != MRP_PROTO_ACTION_NONE))
		mrp_state_machine->mrp_state_machine_ops->mrp_state_machine_perform_protocol_action (mrp_state_machine, protocol_action2);

	return 0;
}
